
// Copyright (c) WanSheng Intelligent Corp. All rights reserved.
// Licensed under the LGPL v3.0 license. 

#include <stdlib.h>
#include <pthread.h>

#include "opcua_plugin.h"
#include "wa_plugin_sdk.h"
#include "logs.h"

typedef struct
{
	BROKER_HANDLE       broker;
	RESTFUL_CONTEXT     restful_context;
	wa_task_scheduler_t task_scheduler;
}OPCUA_HANDLE_DATA;

static void* OPCUA_Plugin_ParseConfigurationFromJson(const char* configuration)
{
	(void)configuration;
	return NULL;
}

static void OPCUA_Plugin_FreeConfiguration(void* configuration)
{
	(void)configuration;
}

static MODULE_HANDLE OPCUA_Plugin_Create(BROKER_HANDLE broker, const void* configuration)
{
	WARNING2("Enter..");
	OPCUA_HANDLE_DATA* result;
	WALOG("Enter %s: broker=[%p]", __FUNCTION__, broker);
	if ((broker == NULL))
	{
		ERROR("invalid arg broker=%p", broker);
		return NULL;
	}

	result = (OPCUA_HANDLE_DATA*)malloc_z(sizeof(OPCUA_HANDLE_DATA));
	if (result == NULL)
	{
		ERROR("unable to malloc");
		return NULL;
	}

	result->broker = broker;

	return result;
}

static void OPCUA_Plugin_Destroy(MODULE_HANDLE module)
{
	WARNING2("Enter..");
	OPCUA_HANDLE_DATA* handleData = (OPCUA_HANDLE_DATA*)module;
	if (handleData->task_scheduler)
		bh_task_destroy_scheduler(handleData->task_scheduler);
	free(handleData);

}

static void OPCUA_Plugin_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle)
{
	static bool name = false;
	if (!name) {
		prctl(PR_SET_NAME, "opcua_plugin");
		name = true;
	}
}

void open_opcua_device(opcua_device* device)
{
	WARNING2("Enter..");
	UA_Client* dc;

	dc = UA_Client_new();

	UA_ClientConfig_setDefault(UA_Client_getConfig(dc));
	if (dc)
	{

		UA_StatusCode retval = UA_Client_connect(dc, device->url.c_str());
		if (retval == UA_STATUSCODE_GOOD)
		{
			WARNING("[%s] PLC has Connected!\n", device->plugin_cfg->device_name);
		}
		else
		{
			WARNING("[%s] Cannot connect PLC! The code is [%X]\n", device->plugin_cfg->device_name, retval);
			UA_Client_delete(dc);
			return;
		}

		device->dc = dc;
	}

	return;
}

static int task_opcuaplc_connect(wa_task_t task)
{
	WARNING2("Enter..");
	int i;
	for (i = 0; i < MAX_OPCUA_DEVICE; i++)
	{
		if (g_opcua_devices[i] == NULL)
		{
			return 0;
		}

		if (g_opcua_devices[i]->dc == NULL)
		{
			open_opcua_device(g_opcua_devices[i]);
		}
	}

	return 0;
}
static void OPCUA_Plugin_Start(MODULE_HANDLE module)
{
	WARNING2("Enter..");
	OPCUA_HANDLE_DATA* handleData = (OPCUA_HANDLE_DATA*)module;

	// initiate the restful context
	RESTFUL_CONTEXT rest_context = WA_InitRestful(handleData->broker, module);
	handleData->restful_context = rest_context;

	// register the resource /mb2 for handling the incomming requests
	WA_RegisterResource(rest_context, "/opcua", opcua_Get, T_Get);
	WA_RegisterResource(rest_context, "/opcua", opcua_Put, T_Put);

	// load the configured devices and setup the internal data structure
	const char* plugin_name = WA_GetModuleName(handleData->broker, module);
	load_device_config(plugin_name);

	// start a new thread for handling the tasks
	handleData->task_scheduler = bh_task_run_new_scheduler();
	// create a regular executed task for reconnecting the devices or buses
	wa_task_t task1 = bh_new_task((char*)"connect_task",
		NULL,
		true,
		1 * 1000,   // repeating task in 1000 ms
		task_opcuaplc_connect);
	bh_schedule_task(handleData->task_scheduler, task1, 0);

	// tell the framework this plugin can be unplugged on the fly
	WA_SetPlugin_Unplugable(handleData->broker, module, true);


}

static const MODULE_API_1 OPCUA_APIS_all =
{
		{MODULE_API_VERSION_1},
		OPCUA_Plugin_ParseConfigurationFromJson,
		OPCUA_Plugin_FreeConfiguration,
		OPCUA_Plugin_Create,
		OPCUA_Plugin_Destroy,
		OPCUA_Plugin_Receive,
		OPCUA_Plugin_Start
};

MODULE_EXPORT const MODULE_API* Module_GetApi(MODULE_API_VERSION gateway_api_version)
{
	WARNING2("Enter..");
	const MODULE_API* api;
	if (gateway_api_version >= OPCUA_APIS_all.base.version)
	{
		api = (const MODULE_API*)&OPCUA_APIS_all;
	}
	else
	{
		api = NULL;
	}

	return api;
}

